﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using DimensionsHelper.Common.Contracts.Table;
using DimensionsHelper.Common.Table;

namespace DimensionsHelper.Services.CsvReader;

public class CsvTable : ITable
{
    private List<ILine> _lines;
    private readonly Dictionary<int, char> _sigColumnToIdMap;
    private readonly Dictionary<char, int> _sigIdToColumnMap;

    private bool _finished;

    public CsvTable(ICell titleCell, CsvTable? lastTable = null, int lastEndIndex = 0)
    {
        TitleCell = titleCell;
        LastEndIndex = lastEndIndex;

        PreviousTable = lastTable;
        PreviousOriginalTable = lastTable;
        if (lastTable != null)
        {
            lastTable.NextTable = this;
            lastTable.NextOriginalTable = this;
        }

        NextTable = null;
        NextOriginalTable = null;

        Columns = 1;
        _lines = new List<ILine>();
        _sigIdToColumnMap = new Dictionary<char, int>();
        _sigColumnToIdMap = new Dictionary<int, char>();
        IsSignificantTestTable = false;
        SignificantTestColumnCharacterLine = null;

        _finished = false;
    }

    public ILine this[int index] => _lines[index];
    public int Count => _lines.Count;
    public int Columns { get; private set; }
    public int LastEndIndex { get; }

    public ITable? PreviousTable { get; internal set; }
    public ITable? NextTable { get; internal set; }
    public ITable? PreviousOriginalTable { get; }
    public ITable? NextOriginalTable { get; private set; }

    public ICell TitleCell { get; }

    public bool IsSignificantTestTable { get; private set; }

    public ILine? SignificantTestColumnCharacterLine { get; private set; }

    public int StartLineIndex => _lines.Count == 0 ? 0 : _lines[0].OriginalIndex;
    public int EndLineIndex => _lines.Count == 0 ? 0 : _lines[^1].OriginalIndex;


    public ILine? GetSpecialLine(LineType lineType)
    {
        return _lines.FirstOrDefault(line => line.TestLineType(lineType));
    }

    public bool TryGetSignificantTestIdColumn(char id, out int column)
    {
        return _sigIdToColumnMap.TryGetValue(id, out column);
    }


    public bool TryGetColumnSignificantTestId(int column, out char id)
    {
        return _sigColumnToIdMap.TryGetValue(column, out id);
    }

    /// <summary>
    ///     向行集合中添加新行，在表格完成后无法添加新行。
    /// </summary>
    /// <param name="line">新行，此方法不会修改行的内容和前后行关系。</param>
    /// <exception cref="System.Exception"></exception>
    public void Add(ILine line)
    {
        if (_finished)
        {
            throw new TableFinishedException();
        }

        if (line.OriginalIndex == TitleCell.Line.OriginalIndex)
        {
            return;
        }

        if (line.OriginalIndex < TitleCell.Line.OriginalIndex)
        {
            if (line.Count > 0 && !line[0].IsEmpty)
            {
                ((CsvLine)line).LineType |= LineType.HeaderSpec;
            }

            ((CsvLine)line).LineType |= LineType.Header;
        }
        else
        {
            if (line is { Count: > 0, NotEmptyCellCount: 1 } && !line[0].IsEmpty)
            {
                ((CsvLine)line).LineType |= LineType.InnerSpec;
            }

            ((CsvLine)line).LineType |= LineType.Body;
        }

        if (line.IsEmpty)
        {
            ((CsvLine)line).LineType |= LineType.Empty;
        }

        if (TitleCell.Line.OriginalIndex - line.OriginalIndex == 1 &&
            line.All(c => c.IsEmpty || (c.Length == 1 && char.IsLetter(c.CharAt(0)))))
        {
            ((CsvLine)line).LineType |= LineType.SigMark;
            IsSignificantTestTable = true;

            // 构建显著性列表
            if (SignificantTestColumnCharacterLine != null)
            {
                throw new Exception($"{nameof(ITable)}中类型是{nameof(LineType.SigMark)}的行只能有一个。");
            }

            foreach (var c in line)
            {
                if (!c.IsEmpty)
                {
                    _sigIdToColumnMap.Add(c.Text[0], c.Index);
                    _sigColumnToIdMap.Add(c.Index, c.Text[0]);
                }
            }

            SignificantTestColumnCharacterLine = line;
        }

        var prevLine = line.PreviousLine;

        if (line.TestLineType(LineType.Body) && prevLine != null && prevLine.TestLineType(LineType.Body))
        {
            if (line.NotEmptyCellCount == 1 && !line[0].IsEmpty && prevLine.IsEmpty && prevLine.Count == line.Count)
            {
                ((CsvLine)line).LineType |= LineType.TailSpec;
            }
            else if (
                IsSignificantTestTable &&
                prevLine.Count > 1 && !prevLine[0].IsEmpty && !prevLine[1].IsEmpty &&
                (line.IsEmpty || (line[0].IsEmpty &&
                                  line.All(c => c.IsEmpty || CsvHelper.IsStringSignificantResult(c.Text)))))
            {
                ((CsvLine)line).LineType |= LineType.SigResult;
                // 如果上一行是数据行，将新的行设置成上一行的 RelativeSignificantTestLine
                ((CsvLine)prevLine).RelativeSignificantTestLine = line;
            }
        }

        if (_lines.Count >= 2 && _lines[^1].OriginalIndex < _lines[^2].OriginalIndex)
        {
            _lines.Sort((r1, r2) => r1.OriginalIndex.CompareTo(r2.OriginalIndex));
        }

        if (prevLine?.RelativeSignificantTestLine == null)
        {
            _lines.Add(line);
        }

        if (line.Count > Columns)
        {
            Columns = line.Count;
        }

        ((CsvLine)line).Table = this;
    }

    /// <summary>
    ///     完成表格，调用此方法后将无法添加新行。
    /// </summary>
    public void Finish()
    {
        if (_finished)
        {
            return;
        }

        while (_lines.Count > 0 && _lines[0].IsEmpty)
        {
            _lines.RemoveAt(0);
        }

        foreach (var line in _lines)
        {
            if ((line.LineType & LineType.Header) > 0 && line.Count > 1 && line[0].IsEmpty && !line.IsEmpty)
            {
                break;
            }

            ((CsvLine)line).LineType |= LineType.BeforeHeader;
        }

        var tailCellsCount = 0;

        for (var i = _lines.Count - 1; i >= 0; i--)
        {
            var row = _lines[i];
            if (row.NotEmptyCellCount == 1 && !row[0].IsEmpty)
            {
                if (tailCellsCount == 0)
                {
                    tailCellsCount = row.Count;
                }

                ((CsvLine)row).LineType |= LineType.AfterBody;
            }
            else if (row.IsEmpty && (tailCellsCount == 0 || (tailCellsCount > 0 && row.Count == tailCellsCount)))
            {
                ((CsvLine)row).LineType |= LineType.AfterBody;
            }
            else
            {
                break;
            }
        }

        Columns = 0;

        foreach (var line in _lines)
        {
            line.RTrim();
            if (line.Count > Columns)
            {
                Columns = line.Count;
            }
        }

        _finished = true;
    }


    public int FindIndex(string firstCellContent)
    {
        return _lines.FindIndex(l => l.FirstOrDefault()?.Text.Equals(firstCellContent) == true);
    }


    private class LineIndexComparer : IComparer<int>
    {
        public static LineIndexComparer Default => new();

        public int Compare(int x, int y)
        {
            // 任意一个是-1，不排序
            if (x == -1 || y == -1)
            {
                return 0;
            }

            return x - y;
        }
    }


    public void OrderBy(ITable table)
    {
        var resultList = new List<KeyValuePair<int, ILine>>();
        // 遍历当前行，将查找到的行放入resultList，未找到的或空白行放入missingList
        foreach (var line in _lines)
        {
            var first = line.FirstOrDefault();

            // 空行、非表侧数据行直接加入结果列表，并且不排序
            if (first is null || string.IsNullOrEmpty(first.Text) || line.TestLineType(~(LineType.Body | LineType.Normal)))
            {
                resultList.Add(new KeyValuePair<int, ILine>(-1, line));
                continue;
            }

            var index = table.FindIndex(first.Text);
            resultList.Add(new KeyValuePair<int, ILine>(index, line));
        }

        _lines = resultList.OrderBy(pair => pair.Key, LineIndexComparer.Default)
            .Select(pair => pair.Value)
            .ToList();
    }


    public IEnumerator<ILine> GetEnumerator()
    {
        return _lines.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}